#pragma once
#include "core_headers.hpp"
#include "Socket.hpp"
#include "Channel.hpp"
#include "Buffer.hpp"
#include "Any.hpp"
#include "EventLoop.hpp"

// 管理链接的模块
// DISCONECTED -- 连接关闭状态；   CONNECTING -- 连接建立成功-待处理状态
// CONNECTED -- 连接建立完成，各种设置已完成，可以通信的状态；  DISCONNECTING -- 待关闭状态
typedef enum
{
	DISCONNECTED, // 连接关闭状态
	CONNECTING,	  // 连接建立成功-待处理状态
	CONNECTED,	  // 连接建立完成，各种设置已完成，可以通信的状态
	DISCONNECTING // 待关闭状态
} Connstate;

class Connection;
using Ptrconnection = std::shared_ptr<Connection>;
using ConnectCallback = std::function<void(const Ptrconnection &)>;
using MessageCallback = std::function<void(const Ptrconnection &, Buffer *)>;
using CloseCallback = std::function<void(const Ptrconnection &)>;
using AnyEventCallback = std::function<void(const Ptrconnection &)>;

class Connection : public std::enable_shared_from_this<Connection>
{
private:
	/*五个channel的事件回调函数*/
	void HandleRead()
	{
		char buff[65536];
		ssize_t ret = _socket.RecvNonBlock(buff, 65535);
		if (ret < 0)
		{
			// 出错了，不能直接关闭
			ERR_LOG("recv error,%d,%s", errno, strerror(errno));
			return ShutdownInLoop();
		}
		// 这里的等于0表示的是没有读取到数据，而并不是连接断开了，连接断开返回的是-1
		// 将数据放入输入缓冲区,写入之后顺便将写偏移向后移动
		_in_buffer.WriteAndPush(buff, ret);
		// 2. 调用message_callback进行业务处理
		if (_in_buffer.ReadableSize() > 0)
		{
			return _message_callback(shared_from_this(), &_in_buffer);
		}
	}
	void HandleWrite()
	{
		ssize_t ret = _socket.SendNonBlock(_out_buffer.ReadPosition(), _out_buffer.ReadableSize());
		if (ret < 0)
		{
			ERR_LOG("send error,%d,%s", errno, strerror(errno));
			// 发送错误就该关闭连接了，并且处理接受缓冲区数据
			if (_in_buffer.ReadableSize() > 0)
			{
				_message_callback(shared_from_this(), &_in_buffer);
			}
			// 然后释放
			return Release();
		}
		// 千万不要忘了，将读偏移向后移动
		_out_buffer.MoveReadOffset(ret);
		if (_out_buffer.ReadableSize() == 0)
		{
			// 关闭写事件监控
			_channels.CloseWriteableEvent();
			if (_state == DISCONNECTING)
			{
				return Release();
			}
		}
		return;
	}
	void HandleError()
	{
		return HandleClose();
	}
	void HandleClose()
	{
		if (_in_buffer.ReadableSize() > 0)
		{
			_message_callback(shared_from_this(), &_in_buffer);
		}
		return Release();
	}
	void HandleEvent()
	{
		if (_enable_inactive_release == true)
		{
			_loop->RefreshTask(_conn_id);
		}
		if (_event_callback)
		{
			_event_callback(shared_from_this());
		}
	}
	// 连接获取之后，所处的状态下要进行各种设置（启动读监控,调用回调函数）
	void EstablishInLoop()
	{
		assert(_state == CONNECTING);
		_state = CONNECTED;
		// 一旦启动读事件监控就有可能会立即触发读事件，如果这时候启动了非活跃连接销毁,所有放入任务队列中
		_channels.EnableReadableEvent();
		if (_connect_callback)
		{
			_connect_callback(shared_from_this());
		}
	}
	// 这个接口并不是实际的发送接口，而只是把数据放到了发送缓冲区，启动了可写事件监控
	void SendInLoop(Buffer &buff)
	{
		if (_state == DISCONNECTED)
		{
			return;
		}
		_out_buffer.WriteBufferAndPush(buff);
		if (_channels.Writeable() == false)
		{
			_channels.EnableWriteableEvent(); // 会触发「写事件」，告诉程序：“可以调用 write/send 发数据了”。
		}
	}
	// 这个关闭操作并非实际的连接释放操作，需要判断还有没有数据待处理，待发送
	void ShutdownInLoop()
	{
		_state = DISCONNECTING;
		if (_in_buffer.ReadableSize() > 0)
		{
			_message_callback(shared_from_this(), &_in_buffer);
		}
		// 要么就是写入数据的时候出错关闭，要么就是没有待发送数据，直接关闭
		if (_out_buffer.ReadableSize() > 0)
		{
			if (_channels.Writeable() == false)
			{
				_channels.EnableWriteableEvent();
			}
		}
		if (_out_buffer.ReadableSize() == 0)
		{
			DEL_LOG("Release");
			return Release();
		}
	}
	// 真正的释放接口
	void ReleaseInLoop()
	{
		// 1. 修改连接状态，将其置为DISCONNECTED
		_state = DISCONNECTED;
		// 2. 移除连接的事件监控
		_channels.Remove();
		// 3. 关闭描述符
		_socket.Close();
		// 4. 如果当前定时器队列中还有定时销毁任务，则取消任务
		if (_loop->hashTimer(_conn_id))
		{
			CancelInactiveReleaseInLoop();
		}
		// 5. 调用关闭回调函数，避免先移除服务器管理的连接信息导致Connection被释放，再去处理会出错，因此先调用用户的回调函数
		if (_close_callback)
		{
			_close_callback(shared_from_this());
		}
		// 移除服务器内部管理的连接信息
		if (_server_closed_callback)
		{
			_server_closed_callback(shared_from_this());
		}
	}
	// 启动非活跃链接销毁
	void EnableInactiveReleaseInLoop(int sec)
	{
		// 1. 将判断标志 _enable_inactive_release 置为true
		_enable_inactive_release = true;
		// 2. 如果当前定时销毁任务已经存在，那就刷新延迟一下即可
		if (_loop->hashTimer(_conn_id))
		{
			return _loop->RefreshTask(_conn_id);
		}
		// 3. 如果不存在定时销毁任务，则新增
		_loop->TaskAdd(_conn_id, sec, std::bind(&Connection::Release, this));
	}
	// 取消非活跃链接销毁
	void CancelInactiveReleaseInLoop()
	{
		_enable_inactive_release = false;
		if (_loop->hashTimer(_conn_id))
		{
			_loop->TaskCancel(_conn_id);
		}
	}
	//
	void UpgradeInLoop(const Any &context,
					   const ConnectCallback &conn,
					   const MessageCallback &mes,
					   const CloseCallback &close,
					   const AnyEventCallback &event)
	{
		_context = context;
		_connect_callback = conn;
		_message_callback = mes;
		_close_callback = close;
		_event_callback = event;
	}

public:
	Connection(uint64_t id, int sockfd, EventLoop *loop)
		: _conn_id(id), _sockfd(sockfd), _enable_inactive_release(false),
		  _loop(loop), _state(CONNECTING), _socket(_sockfd), _channels(_loop, _sockfd)
	{
		_channels.SetReadCallback(std::bind(&Connection::HandleRead, this));
		_channels.SetWriteCallback(std::bind(&Connection::HandleWrite, this));
		_channels.SetCloseCallback(std::bind(&Connection::HandleClose, this));
		_channels.SetErrorCallback(std::bind(&Connection::HandleError, this));
		_channels.SetEventCallback(std::bind(&Connection::HandleEvent, this));
	}
	~Connection()
	{
		INF_LOG("RELEASE CONNECTION:%p", this);
	}
	// 获取管理的文件描述符
	int Fd()
	{
		return _sockfd;
	}
	int Id()
	{
		return _conn_id;
	}
	// 是否处于CONNECTED状态
	bool Connected()
	{
		return _state == CONNECTED;
	}
	// 设置上下文--连接建立完成时进行调用
	void SetContext(const Any &context)
	{
		_context = context;
	}
	// 获取上下文，返回的是指针
	Any *GetContext()
	{
		return &_context;
	}
	void SetConnectCallback(const ConnectCallback &cd)
	{
		_connect_callback = cd;
	}
	void SetMessageCallback(const MessageCallback &cd)
	{
		_message_callback = cd;
	}
	void SetCloseCallback(const CloseCallback &cd)
	{
		_close_callback = cd;
	}
	void SetAnyEventCallback(const AnyEventCallback &cd)
	{
		_event_callback = cd;
	}
	void SetServeCloseCallback(const CloseCallback &cd)
	{
		_server_closed_callback = cd;
	}
	// 建立链接后进行各种设置
	void Establish()
	{
		_loop->RunInLoop(std::bind(&Connection::EstablishInLoop, this));
	}
	// 发生数据，将数据发送到缓冲区中，启动写事件监控
	void Send(const char *date, size_t len)
	{
		// 外界传⼊的data，可能是个临时的空间，我们现在只是把发送操作压⼊了任务池，有可能并没有被⽴即执⾏
        // 因此有可能执⾏的时候，data指向的空间有可能已经被释放了。
		Buffer buff;
		buff.WriteAndPush(date,len);
		_loop->RunInLoop(std::bind(&Connection::SendInLoop, this, buff));
	}
	// 关闭链接，并不是真正的关闭链接，因为需要判断关闭连接前有没有数据待发送处理
	void Shutdown()
	{
		_loop->RunInLoop(std::bind(&Connection::ShutdownInLoop, this));
	}
	// 真正释放
	void Release()
	{
		// 放入任务队列是因为，如果链接释放了，还要进行操作而导致崩溃，所以要压入队列，
		// 1234，如果1是定时销毁任务，释放了，就会把234对应链接释放，在进行234事件操作就会引发崩溃
		_loop->QueueInLoop(std::bind(&Connection::ReleaseInLoop, this));
	}
	// 启动非活跃销毁
	void EnableInactiveRelease(int sec)
	{
		_loop->RunInLoop(std::bind(&Connection::EnableInactiveReleaseInLoop, this, sec));
	}
	// 取消非活跃销毁
	void CancelInactiveRelease()
	{
		_loop->RunInLoop(std::bind(&Connection::CancelInactiveReleaseInLoop, this));
	}
	// 切换协议
	// 切换协议---重置上下文以及阶段性回调处理函数 -- 而是这个接口必须在EventLoop线程中立即执行
	// 防备新的事件触发后，处理的时候，切换任务还没有被执行--会导致数据使用原协议处理了
	void Upgrade(const Any &context,
				 const ConnectCallback &conn,
				 const MessageCallback &mes,
				 const CloseCallback &close,
				 const AnyEventCallback &event)
	{
		// 必须保证在当前线程，
		_loop->AssertInLoop();
		_loop->RunInLoop(std::bind(&Connection::UpgradeInLoop, this, context, conn, mes, close, event));
	}

private:
	uint64_t _conn_id;			   // 链接的唯一id
	int _sockfd;				   // 链接管理的文件描述符
	Socket _socket;				   // 套接字操作管理
	bool _enable_inactive_release; // 是否启动非活跃链接销毁 默认为false
	Connstate _state;			   // 链接所处的状态
	EventLoop *_loop;			   // 连接所关联的一个EventLoop
	Channel _channels;			   // 链接的事件管理
	Buffer _in_buffer;			   // 输入缓冲区，从socket接受的数据放入输入缓冲区
	Buffer _out_buffer;			   // 输出缓冲区，存放要发送的数据
	Any _context;				   // 请求的接受处理上下文
	ConnectCallback _connect_callback;
	MessageCallback _message_callback;
	CloseCallback _close_callback;
	AnyEventCallback _event_callback;
	/*组件内的连接关闭回调--组件内设置的，因为服务器组件内会把所有的连接管理起来，一旦某个连接要关闭*/
	/*就应该从管理的地方移除掉自己的信息*/
	CloseCallback _server_closed_callback;
};